package com.panopset.gui;

import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;

import javax.swing.JPanel;

import static com.panopset.Util.*;

/**
 * Off-screen graphic world, (Osgw in Java), term borrowed from my days at
 * Apple, this does essentially the same thing, provide a flicker free place to
 * do animation.
 *
 * See TestOsgw to see a simple example of how to use it.
 *
 * @author Karl Dinwiddie
 *
 */
public abstract class Osgw {

    /**
     * Default sleep cycle.
     */
    public static final int DEFAULT_SLEEP_CYCLE = 60;

    /**
     * Width.
     */
    private final int w;

    /**
     * Height.
     */
    private final int h;

    /**
     * Sleep cycle.
     */
    private int sleepCycle = DEFAULT_SLEEP_CYCLE;

    /**
     * Active flag.
     */
    private static boolean isActive = true;

    /**
     * Stop all Osgw animation threads.
     */
    public static void close() {
        isActive = false;
    }

    /**
     * @return true if active.
     */
    public static boolean isActive() {
        return isActive;
    }

    /**
     * Paint a cycle.
     *
     * @param g
     *            Graphics 2D used to paint the cycle.
     */
    public abstract void paintCycle(Graphics2D g);

    /**
     * @param width
     *            Width.
     * @param height
     *            Height.
     */
    public Osgw(final int width, final int height) {
        this.w = width;
        this.h = height;
    }

    /**
     * @return Width.
     */
    public final int getWidth() {
        return w;
    }

    /**
     * @return Height.
     */
    public final int getHeight() {
        return h;
    }

    /**
     * @param dim
     *            Dimension.
     */
    public Osgw(final Dimension dim) {
        this(dim.width, dim.height);
    }

    /**
     * Set the time to sleep between refresh cycles in milliseconds, default is
     * 60.
     *
     * @param value
     *            in millis.
     */
    public final void setSleepCycle(final int value) {
        this.sleepCycle = value;
    }

    /**
     * @return Panel that contains the animation.
     */
    public final JPanel getPanel() {
        if (panel == null) {
            panel = new OSGWPanel();
            panel.setPreferredSize(new Dimension(w, h));
        }
        return panel;
    }

    /**
     * Panel.
     */
    private JPanel panel;

    /**
     * Image.
     */
    private Image image;

    /**
     * Panel that renders the animation.
     */
    final class OSGWPanel extends JPanel {

        @Override
        public void paint(final Graphics g) {
            if (image == null) {
                super.paint(g);
                image = panel.createImage(w, h);
                new Thread(new Runnable() {

                    @Override
                    public void run() {
                        while (isActive()) {
                            try {
                                paintCycle((Graphics2D) image.getGraphics());
                            } catch (Throwable t) {
                                log(t);
                                close();
                            }
                            panel.repaint();
                            try {
                                Thread.sleep(sleepCycle);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                    }

                }, "Graphix").start();
            } else {
                g.drawImage(image, 0, 0, null);
            }
        }

        /**
         * Serial version id.
         */
        private static final long serialVersionUID = 1L;
    }
}
